﻿using Canteen.Core.Common;
using Canteen.Core.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Authentication;
using System.Security.Claims;
using System.Threading.Tasks;

namespace Canteen.Core.Services
{
    /// <summary>
    /// 商家
    /// </summary>
    public class StoreServiceImplement : IStoreService
    {
        #region 构造函数
        private readonly CanteenDbContext dbContext;
        private readonly HttpContext httpContext;
        private readonly IRedisCacheManager redisCacheManager;
        private readonly IJwtAuth jwtAuth;

        public StoreServiceImplement(CanteenDbContext dbContext,
            IHttpContextAccessor httpContext,
            IRedisCacheManager redisCacheManager,
            IJwtAuth jwtAuth)
        {
            this.dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            this.httpContext = httpContext?.HttpContext ?? throw new ArgumentNullException(nameof(httpContext));
            this.redisCacheManager = redisCacheManager ?? throw new ArgumentNullException(nameof(redisCacheManager));
            this.jwtAuth = jwtAuth ?? throw new ArgumentNullException(nameof(jwtAuth));
        }
        #endregion

        private T RedisCache<T>(Func<IDatabase, T> func)
        {
            return func(redisCacheManager.RedisCache(c => c, 5));
        }

        public async Task<Store> CreateStore(Store store)
        {
            await dbContext.Stores.AddAsync(store);
            if (await dbContext.SaveChangesAsync() > 0) return store;
            throw new Exception("保存商家信息失败");
        }
        public async Task<Store> UpdateStore(Store store)
        {
            //先查询出来，因为操作被追踪的数据
            var storeEntiy = await dbContext.Stores.SingleOrDefaultAsync(s=>s.Id==store.Id);
            //修改Name属性，被追踪的league状态属性就会变为Modify
            storeEntiy.Name = store.Name;
            storeEntiy.Address = store.Address;
            storeEntiy.Leader = store.Leader;
            storeEntiy.Remarks = store.Remarks;
            storeEntiy.StoreImgUrl = store.StoreImgUrl;
            storeEntiy.OpeningDate = store.OpeningDate;
            storeEntiy.Phone = store.Phone;
            storeEntiy.UpdateTime = DateTime.Now;
            //执行数据库操作
            if (await dbContext.SaveChangesAsync() > 0) return storeEntiy;
            throw new Exception("修改商家信息失败");
        }
        public async Task<bool> DeleteStore(int id)
        {
            var store =await dbContext.Stores.SingleOrDefaultAsync(l => l.Id == id);
            dbContext.Stores.Remove(store);//删除单个Leagues
            return await dbContext.SaveChangesAsync() > 0;
        }
        public async Task<bool> ChangeStoreVerify(int id, int verify)
        {
            //先查询出来，因为操作被追踪的数据
            var storeEntiy = await dbContext.Stores.SingleOrDefaultAsync(s => s.Id == id);
            //修改Name属性，被追踪的league状态属性就会变为Modify
            storeEntiy.Verify = verify;
            storeEntiy.UpdateTime = DateTime.Now;
            //执行数据库操作
            return await dbContext.SaveChangesAsync() > 0;
        }

        public async Task<bool> ChangeStoreStatus(int id, int status)
        {
            //先查询出来，因为操作被追踪的数据
            var storeEntiy = await dbContext.Stores.SingleOrDefaultAsync(s => s.Id == id);
            //修改Name属性，被追踪的league状态属性就会变为Modify
            storeEntiy.Status = status;
            storeEntiy.UpdateTime = DateTime.Now;
            //执行数据库操作
            return await dbContext.SaveChangesAsync() > 0;
        }
        public async Task<Store> SelectStoreById(int id)
        {
            return await dbContext.Stores.SingleOrDefaultAsync(l => l.Id.Equals(id));
        }
        public async Task<Store> SelectStoreByPhone(string phone)
        {
            return await dbContext.Stores.Where(u => u.Phone.Equals(phone)).FirstOrDefaultAsync();
        }
        public async Task<Store> SelectStoreByPhoneAndPwd(string phone, string passWord)
        {
            return await dbContext.Stores.Where(u => u.Phone.Equals(phone) && u.PassWord.Equals(passWord)).FirstOrDefaultAsync();
        }

        public async Task<PageList<Store>> GetPageList(StorePageParameters parameters)
        {
            var query = dbContext.Stores.AsQueryable();
            if (!string.IsNullOrWhiteSpace(parameters.Name))
            {
                query = query.Where(m => m.Name.Contains(parameters.Name.Trim()));
            }
            if (!string.IsNullOrWhiteSpace(parameters.Phone))
            {
                query = query.Where(m => m.Phone.Equals(parameters.Phone.Trim()));
            }
            return await PageList<Store>.CreateAsync(query.OrderByDescending(o => o.CreateTime),
                parameters.Current,
                parameters.PageSize);
        }

        public async Task<bool> ExistStoreByRegister(Store store)
        {
            return (await dbContext.Stores.Where(s => s.Name == store.Name && s.Phone == store.Phone).CountAsync()) > 0;
        }


        public int StoreId {
            get
            {
                string uid = httpContext.User.FindFirstValue(JwtRegisteredClaimNames.Sid);
                if (string.IsNullOrEmpty(uid)) return 0;
                if (int.TryParse(uid, out int reval)) return reval;
                return reval;
            }
        }
        public async Task<bool> IsLogin()
        {
            if (StoreId<=0) return false;
            return await RedisCache(cache => cache.KeyExistsAsync(StoreId.ToString()));
        }

        public async Task<Store> GetStoreLogin()
        {
            if (StoreId <= 0) throw new ArgumentNullException();
            string obj = await RedisCache(cache => cache.StringGetAsync(StoreId.ToString()));
            if (string.IsNullOrWhiteSpace(obj)) return default;
            if (!obj.Contains("Id")) throw new AuthenticationException("获取登录信息失败");
            return JsonConvert.DeserializeObject<Store>(obj);
        }

        public async Task<JwtAuthResult> SetStoreLogin(Store store)
        {
            if (store is null) throw new ArgumentNullException(nameof(store));

            var expires = TimeSpan.FromDays(jwtAuth.Option.Expires);
            //var refreshExpires = TimeSpan.FromDays(jwtAuth.Option.RefreshExpires);
            string refreshToken = jwtAuth.GenerateRefreshToken(); //生成刷新token
            var cache = RedisCache(cache => cache);
            await cache.StringSetAsync(store.Id.ToString(), JsonConvert.SerializeObject(store), expires);//保存用户信息
            //var saveRefreshToken = cache.StringSetAsync(refreshToken, store.Id.ToString(), refreshExpires); //保存刷新token
            //var saveUserTokenList = cache.ListLeftPushAsync(store.Id.ToString(), refreshToken);//添加[刷新token]到key为User.Id的List中

            //await Task.WhenAll(saveUserForCode, saveRefreshToken);//, saveUserTokenList

            //if (saveUserForCode.Result && saveRefreshToken.Result)//&& saveUserTokenList.Result > 0
            //{
                var Claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Role,"admin"),
                    new Claim(JwtRegisteredClaimNames.Sid, store.Id.ToString())
                };
                return new JwtAuthResult
                {
                    AccessToken = jwtAuth.GenerateToken(Claims),
                    RefreshToken = refreshToken
                };
            //}
            //throw new Exception("登录凭证设置失败");
        }

        public async Task RemoveStoreLogin()
        {
            var store = await GetStoreLogin();
            string id = store.Id.ToString();

            var cache = RedisCache(cache => cache);
            var refreshTokenList = await cache.ListRangeAsync(id);//获取用户的刷新token列表
            var list = refreshTokenList.Select(redisKey => (RedisKey)redisKey.ToString()).ToArray();

            var delRefreshToken = cache.KeyDeleteAsync(list);//批量删除刷新token
            var delUserForCode = cache.KeyDeleteAsync(id);//删除用户信息
            var delUserTokenList = cache.KeyDeleteAsync(id);//删除[刷新token]key为User.Id的List
            await Task.WhenAll(delRefreshToken, delUserForCode, delUserTokenList);

            if (!(delRefreshToken.Result > 0 && delUserForCode.Result && delUserTokenList.Result)) throw new Exception("删除登录凭证失败");
        }

        public async Task RemoveRefreshToken(int storeId, string refreshToken)
        {
            await RedisCache(cache => cache.ListRemoveAsync(storeId.ToString(), refreshToken));
        }

        
    }
}
